package com.block.datapush.form.item.util;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.Cipher;
import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Mr.Zheng
 * @date 2014年8月22日 下午1:44:23
 */
//@Slf4j
public final class ZYRSAUtils {
	private static final String RSA = "RSA";
	private static final String SIGN_ALGORITHMS = "SHA1WithRSA";

	/**
	 * 随机生成RSA密钥对(默认密钥长度为1024)
	 * 
	 * @return
	 */
	public static KeyPair generateRSAKeyPair() {
		return generateRSAKeyPair(1024);
	}

	/**
	 * 随机生成RSA密钥对
	 * 
	 * @param keyLength
	 *            密钥长度，范围：512～2048<br>
	 *            一般1024
	 * @return
	 */
	public static KeyPair generateRSAKeyPair(int keyLength) {
		try {
			KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA);
			kpg.initialize(keyLength);
			return kpg.genKeyPair();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return null;
		}
	}

	public static int getKeySize(PublicKey publicKey) {
		RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
		return rsaPublicKey.getModulus().bitLength();
	}

	public static int getKeySize(PrivateKey privateKey) {
		RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
		return rsaPrivateKey.getModulus().bitLength();
	}

	/**
	 * 用公钥加密 <br>
	 * 每次加密的字节数，不能超过密钥的长度值减去11
	 * 
	 * @param data
	 *            需加密数据的byte数据
	 * @param publicKey
	 *            公钥
	 * @return 加密后的byte型数据
	 */
	public static byte[] encryptData(byte[] data, PublicKey publicKey) {
		try {
			Cipher cipher = Cipher.getInstance(RSA);
			// 编码前设定编码方式及密钥
			cipher.init(Cipher.ENCRYPT_MODE, publicKey);
			int keyBit = getKeySize(publicKey);
			int inputLen = data.length;
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			int offSet = 0;
			int step = keyBit / 8 - 11;

			for (int i = 0; inputLen - offSet > 0; offSet = i * step) {
				byte[] cache;
				if (inputLen - offSet > step) {
					cache = cipher.doFinal(data, offSet, step);
				} else {
					cache = cipher.doFinal(data, offSet, inputLen - offSet);
				}

				out.write(cache, 0, cache.length);
				++i;
			}

			byte[] encryptedData = out.toByteArray();
			out.close();
			return encryptedData;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 用私钥解密
	 * 
	 * @param encryptedData
	 *            经过encryptedData()加密返回的byte数据
	 * @param privateKey
	 *            私钥
	 * @return
	 */
	public static byte[] decryptData(byte[] encryptedData, PrivateKey privateKey) {
		try {
			Cipher cipher = Cipher.getInstance(RSA);
			cipher.init(Cipher.DECRYPT_MODE, privateKey);
			int keyBit = getKeySize(privateKey);
			int inputLen = encryptedData.length;
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			int offSet = 0;
			int step = keyBit / 8;

			for (int i = 0; inputLen - offSet > 0; offSet = i * step) {
				byte[] cache;
				if (inputLen - offSet > step) {
					cache = cipher.doFinal(encryptedData, offSet, step);
				} else {
					cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
				}

				out.write(cache, 0, cache.length);
				++i;
			}

			byte[] decryptedData = out.toByteArray();
			out.close();
			return decryptedData;
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * 从字符串中加载公钥
	 * 
	 * @param publicKeyStr
	 *            公钥数据字符串
	 * @throws Exception
	 *             加载公钥时产生的异常
	 */
	public static PublicKey loadPublicKey(String publicKeyStr) throws Exception {
		try {
			byte[] buffer = ZYBase64Utils.decode(publicKeyStr);
			KeyFactory keyFactory = KeyFactory.getInstance(RSA);
			X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
			return (RSAPublicKey) keyFactory.generatePublic(keySpec);
		} catch (NoSuchAlgorithmException e) {
			throw new Exception("无此算法");
		} catch (InvalidKeySpecException e) {
			throw new Exception("公钥非法");
		} catch (NullPointerException e) {
			throw new Exception("公钥数据为空");
		}
	}

	/**
	 * 从字符串中加载私钥<br>
	 * 加载时使用的是PKCS8EncodedKeySpec（PKCS#8编码的Key指令）。
	 * 
	 * @param privateKeyStr
	 * @return
	 * @throws Exception
	 */
	public static PrivateKey loadPrivateKey(String privateKeyStr) throws Exception {
		try {
			byte[] buffer = ZYBase64Utils.decode(privateKeyStr);
			// X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
			PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
			KeyFactory keyFactory = KeyFactory.getInstance(RSA);
			return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
		} catch (NoSuchAlgorithmException e) {
			throw new Exception("无此算法");
		} catch (InvalidKeySpecException e) {
			throw new Exception("私钥非法");
		} catch (NullPointerException e) {
			throw new Exception("私钥数据为空");
		}
	}

	/**
	 * 从文件中输入流中加载公钥
	 * 
	 * @param in
	 *            公钥输入流
	 * @throws Exception
	 *             加载公钥时产生的异常
	 */
	public static PublicKey loadPublicKey(InputStream in) throws Exception {
		try {
			return loadPublicKey(readKey(in));
		} catch (IOException e) {
			throw new Exception("公钥数据流读取错误");
		} catch (NullPointerException e) {
			throw new Exception("公钥输入流为空");
		}
	}

	/**
	 * 从文件中加载私钥
	 * 
	 * @param in
	 *            私钥文件名
	 * @return 是否成功
	 * @throws Exception
	 */
	public static PrivateKey loadPrivateKey(InputStream in) throws Exception {
		try {
			return loadPrivateKey(readKey(in));
		} catch (IOException e) {
			throw new Exception("私钥数据读取错误");
		} catch (NullPointerException e) {
			throw new Exception("私钥输入流为空");
		}
	}

	/**
	 * 读取密钥信息
	 * 
	 * @param in
	 * @return
	 * @throws IOException
	 */
	private static String readKey(InputStream in) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(in));
		String readLine = null;
		StringBuilder sb = new StringBuilder();
		while ((readLine = br.readLine()) != null) {
			if (readLine.charAt(0) == '-') {
				continue;
			} else {
				sb.append(readLine);
				sb.append('\r');
			}
		}

		return sb.toString();
	}

	/**
	 * 使用私钥对数据进行加密签名
	 * 
	 * @param encryptByte
	 *            数据
	 * @param privateKey
	 *            私钥
	 * @return 加密后的签名
	 */
	public static String rsaSign(byte[] encryptByte, PrivateKey privateKey) {
		try {
			Signature signature = Signature.getInstance(SIGN_ALGORITHMS);
			signature.initSign(privateKey);
			signature.update(encryptByte);
			byte[] signed = signature.sign();
			return ZYBase64Utils.encode(signed);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 使用公钥判断签名是否与数据匹配
	 * 
	 * @param encryptByte
	 *            数据
	 * @param bs
	 *            签名
	 * @param publicKey
	 *            公钥
	 * @return 是否篡改了数据
	 */
	public static boolean doCheck(byte[] encryptByte, byte[] bs, PublicKey publicKey) {
		try {
			Signature signature = Signature.getInstance(SIGN_ALGORITHMS);
			signature.initVerify(publicKey);
			signature.update(encryptByte);
			boolean bverify = signature.verify(bs);
			return bverify;

		} catch (Exception e) {
			e.printStackTrace();
		}

		return false;
	}

	/**
	 * 组装http请求的入参
	 *
	 * @return
	 */
	public static Map<String, Object> encodeParam(String source, String PUCLIC_KEY, String PRIVATE_KEY) {
		// http请求入参组装
		Map<String, Object> data = new HashMap<>();
		// 业务参数json字符串组装
		try {
			// 加密部分demo
			// 从字符串中得到公钥
			PublicKey publicKey = ZYRSAUtils.loadPublicKey(PUCLIC_KEY);

//			log.info("需加密的明文：" + source);
			byte[] encryptByte = ZYRSAUtils.encryptData(source.getBytes("utf-8"), publicKey);
			// 为了方便观察把加密后的数据用base64加密转一下，要不然看起来是乱码,所以解密是也是要用Base64先转换
			String afterencrypt = ZYBase64Utils.encode(encryptByte);
//			log.info("加密后的密文：" + afterencrypt);// java.net.URLEncoder.encode(afterencrypt,"utf-8"));
			data.put("content", afterencrypt);
			// 从字符串中得到私钥
			PrivateKey privateKey = ZYRSAUtils.loadPrivateKey(PRIVATE_KEY);
			// 加签
			String signStr = ZYRSAUtils.rsaSign(encryptByte, privateKey);
//			log.info("加签后的签名：" + signStr);
			data.put("sign", signStr);
		} catch (Exception e) {
			e.printStackTrace();
		}
//		log.info("加密后的json：" + data);
		return data;
	}

	/**
	 * 解密、验签，获取接口业务入参
	 *
	 * @param data
	 *            http请求入参
	 * @return
	 */
	public static JSONObject decodeParam(Map<String, String> data, String PUCLIC_KEY, String PRIVATE_KEY) {
		String afterencrypt = data.get("content");
//		log.info("需解密的密文：" + afterencrypt);// java.net.URLEncoder.encode(afterencrypt,"utf-8"));
		String signStr = data.get("sign");
		JSONObject paramJson = new JSONObject();
		try {
			// 从字符串中得到公钥
			PublicKey publicKey = ZYRSAUtils.loadPublicKey(PUCLIC_KEY);
			// 从字符串中得到私钥
			PrivateKey privateKey = ZYRSAUtils.loadPrivateKey(PRIVATE_KEY);
			// 因为RSA加密后的内容经Base64再加密转换了一下，所以先Base64解密回来再给RSA解密
			byte[] decryptByte = ZYRSAUtils.decryptData(ZYBase64Utils.decode(afterencrypt), privateKey);
			String decryptStr = new String(decryptByte);
//			log.info("解密后的明文：" + decryptStr);
			paramJson = JSONObject.parseObject(decryptStr);
			// 验签
			boolean result = ZYRSAUtils.doCheck(ZYBase64Utils.decode(afterencrypt), ZYBase64Utils.decode(signStr), publicKey);
//			log.info("验签的结果：" + result);
			if (!result) {
//				log.info("验签失败");
				return null;
			}
		} catch (Exception e) {
			e.printStackTrace();
			paramJson = null;
		}
		return paramJson;
	}

	private final static String PUCLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA018LjVjqaEj58qOwpdEteQ26Uhs4xEdZOlftvRAbOTQsAAFZ/cjOxvGpEgI2y66bWguEk9SEg/xea9v0llnzbKmPNul6BhqUpvH7bWI1OFgv6IPJIxIpgZthXxyY25HmbR6hFyszgYCKlZiKWh23fVj0mkkd5Xm+4CmZ+8ewVeXG8N4A1tKTLo+4bKsN+WrHN4St+UeaNEBSwZ2XlaR+FFeLvZ9oR6z78+5aXL8PohyW1EwkY4WFByAgce6Efsszw7CMh2kOG6Xm/WrVesF5JZXorOHO2X4p6EO4j0zb2wvI3EOOaRIIm9PKzkKedzgPGY5uXh2E47ewg1if7M8KbwIDAQAB";
	private final static String PRIVATE_KEY = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDTXwuNWOpoSPnyo7Cl0S15DbpSGzjER1k6V+29EBs5NCwAAVn9yM7G8akSAjbLrptaC4ST1ISD/F5r2/SWWfNsqY826XoGGpSm8fttYjU4WC/og8kjEimBm2FfHJjbkeZtHqEXKzOBgIqVmIpaHbd9WPSaSR3leb7gKZn7x7BV5cbw3gDW0pMuj7hsqw35asc3hK35R5o0QFLBnZeVpH4UV4u9n2hHrPvz7lpcvw+iHJbUTCRjhYUHICBx7oR+yzPDsIyHaQ4bpeb9atV6wXklleis4c7ZfinoQ7iPTNvbC8jcQ45pEgib08rOQp53OA8Zjm5eHYTjt7CDWJ/szwpvAgMBAAECggEBAMj6x7Y3xHMyaI9hO7b/JZjEB9TIU3q2SM5kCwbJSMlkpS3AGzxncs3RvKwF9OpI9ShyaTZTTUlebr1qIK/RiddZhnZ1G/Sv04bF8gb46ZfQqoUQwfCZ9WNqRfS08Vdd7Fb3V7768feCLMO/jlXPicgjDDmjqkynLWphxCuWnUGeBtzIg+J47PJuSW8el/xoBNdWgaJhfO+7E4mjT89ixH/Wv1u5a4dEOq3tzcG3EsFd1gFZK22A3Lae/88JsmB5cxU0wtsKOSBmTBo2HAd1J1IrA+Wp0iGEn4WB73sGdSgfE+LwzmceEO7kgqxCwBAkNi6LG6hQfDpIUSTBV0pvJGECgYEA60SjpNV92KZT0CmhvvxCnkBFSqxBFA5HbJCwn8SC/W0iHRR3SmzNgYC0McpqG6QGE1c9IijleGqZq5si+nwTM3U45VS2c5DkQTWQX5Cz9KHyjOumgHtxgan1uwTJXb7TZCl5RFr3Cyk55KHlR7vsMplApNPcy9R/mfK81v319t8CgYEA5f9SecTUtE6M5WaiKh9Mvf7+ggF3uzVbdggDWwz/KKdkf6d/d6cFUJAKMm7kNf8c0GNuY4cr3ZEgUljGcogi1fHQBgPjk40tssbcx92Zp/ZTAbN+wTa18SZ965vuuA2vRWoowDsSSN2NGp53jkUxnfjNn8l+RfIV/RkczCA/LnECgYBaPd/PMByvZ00qwW1LRpN/BAQQGnZz7O40lGNLRSNBdgTpxpnjMEy7adS0bIG6zHWQIZxPaARs5ea/zifikIkE6efC/3VkaqVC5frUzTpZkWzp5NHe4nqtU7Xv4Gwkyzdmagob0BCGdW/BZN//XfPBjMsg7ir4/C9dq6AHjy4zXQKBgQDjgljXUzkE0t7U3V10RVnoZ6DigQkrCnDapLqNCqIsWMk07T9N4M2FD7bGhKMCNsUN7g14LnHZzoEmc+J50OiEsZqRGK+5eWHHAqfKnQ4t85/QgvnTrobzcJwGwEy3i/HgvE5rOQOxSFcYCcFB3nhiR4c/lFVwYzQVFQ5DbXKSQQKBgQDoyv30r9OI0UfqH02yOtrlgPn2jGnclB0nuDCifn731CtNI3Fo1UOzsvQjbFW3rFGtrocGJre5SXpWoMZsEM2mXtlnpjCQS8QlAnGF18Z0cyJKJ2QeCQJ5IAyC7nkNAB67kbL78wzHwfgCla9GN3Rb9Y1Spxy8BhgIinyP9bIyRw==";


}
